﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Windows.Forms;
using System.Xml.XPath;
using WindowsLive.Writer.Api;

namespace SJM.DeliciousBookmarks
{

    [WriterPlugin("F715B831-9B6E-4283-8A73-CD09A14EB01A",
                     "Delicious Bookmarks",
                     HasEditableOptions = true,
                     ImagePath = "DeliciousIcon.png",
                     PublisherUrl = "http://www.sjmdev.com/",
                     Description = "Insert your up-to-date delicious bookmarks")]
    [InsertableContentSource("Delicious Bookmarks")]
    public class DeliciousBookmark : ContentSource
    {

        #region Constants

        private const string DELICIOUS_FEEDS_URL = "http://feeds.delicious.com/v2/rss/";

        private const string SJM_BOOKMARK_PLUGIN = "SJM Dev Delicious Bookmarks";

        private const int TIMEOUT_SECONDS = 5;

        #endregion

        #region Fields

        private int _amountResults;

        private DateTime _lastUsedDate;

        Options _options = new Options();

        private string _parameters;

        private IProperties _pluginOptions;

        private string _username;

        #endregion

        #region Public Methods

        /// <summary>
        /// Main method - Called when plug in is run
        /// </summary>
        /// <param name="dialogOwner">The dialog owner.</param>
        /// <param name="newContent">The new content.</param>
        /// <returns></returns>
        public override DialogResult CreateContent(IWin32Window dialogOwner, ref string newContent)
        {
            DialogResult result = DialogResult.OK;

            if (String.IsNullOrEmpty(_username))
            {
                this.ShowEditOptions();
            }

            SetOptions(_pluginOptions);

            newContent = GetDeliciousFeed(_username, _parameters, _lastUsedDate);

            _lastUsedDate = DateTime.Now;
            _options.LastUsedOn = _lastUsedDate;
            SaveOptions();

            return result;
        }

        /// <summary>
        /// Edits the options, launches WinForm dialogue to set options.
        /// </summary>
        /// <param name="dialogOwner">The dialog owner.</param>
        public override void EditOptions(IWin32Window dialogOwner)
        {
            this.ShowEditOptions();
        }

        /// <summary>
        /// Initializes the specified plugin options.
        /// </summary>
        /// <param name="pluginOptions">The plugin options.</param>
        public override void Initialize(IProperties pluginOptions)
        {
            base.Initialize(pluginOptions);

            SetOptions(pluginOptions);
        }

        #endregion

        #region Private Methods

        /// <summary>
        /// Adds matching results to lists.
        /// </summary>
        /// <param name="titles">The delicious titles.</param>
        /// <param name="urls">The urls.</param>
        /// <param name="nodes">Current node.</param>
        private static void AddToLists(List<string> titles, List<string> urls, XPathNodeIterator nodes)
        {
            nodes.Current.MoveToParent();
            nodes.Current.MoveToChild("title", "");
            titles.Add(nodes.Current.Value);

            nodes.Current.MoveToParent();
            nodes.Current.MoveToChild("link", "");
            urls.Add(nodes.Current.Value);
        }

        /// <summary>
        /// Formats the results to an unordered list.
        /// </summary>
        /// <param name="numResults">Number of results.</param>
        /// <param name="titles">Delicious titles list object.</param>
        /// <param name="urls">Delicious urls list object.</param>
        private static StringBuilder FormatResults(int numResults, List<string> titles, List<string> urls)
        {
            StringBuilder sb = new StringBuilder();

            sb = sb.Append("<ul>");

            for (int i = 0; i < numResults; i++)
            {
                sb = sb.AppendFormat("<li><a rel=\"nofollow\" href=\"{0}\">{1}</a></li>", urls[i], titles[i]);
            }

            sb = sb.Append("</ul>");

            return sb;
        }

        /// <summary>
        /// Gets the delicious feed.
        /// </summary>
        /// <param name="username">Delicious username.</param>
        /// <param name="parameters">Parameters to filter results.</param>
        /// <param name="lastPostTime">The last post time.</param>
        /// <returns></returns>
        private string GetDeliciousFeed(string username, string parameters, DateTime lastPostTime)
        {
            string uri = DELICIOUS_FEEDS_URL;
            string results = String.Format("?count={0}", _amountResults);
            uri = string.IsNullOrEmpty(parameters) ? uri + username + results : String.Format("{0}{1}/{2}{3}", uri, username, parameters, results);
            int numResults = 0;
            List<string> titles = new List<string>(0);
            List<string> urls = new List<string>(0);
            StringBuilder sb = new StringBuilder();

            PluginHttpRequest request = new PluginHttpRequest(uri);
            Stream s = request.GetResponse(TIMEOUT_SECONDS * 1000);
            if (s == null)
                throw new InvalidOperationException(String.Format("Couldn't retrieve data from {0}", uri));
            //StreamReader sr = new StreamReader(s);
            //MessageBox.Show(sr.ReadToEnd());

            XPathNavigator navigator = new XPathDocument(uri).CreateNavigator();
            XPathExpression expression = navigator.Compile("/rss/channel/item");
            XPathNodeIterator nodes = navigator.Select(expression);

            while (nodes.MoveNext())
            {
                nodes.Current.MoveToChild("pubDate", "");
                string orig = nodes.Current.Value;
                string pubDate = string.Format("{0:dd/MM/yyyy}", DateTime.Parse(orig.Remove(orig.IndexOf(" +"))));

                if (DateTime.Parse(pubDate) > lastPostTime)
                {
                    AddToLists(titles, urls, nodes);
                    numResults++;
                }
            }

            if (numResults > 0)
            {
                sb = FormatResults(numResults, titles, urls);
            }
            else
            {
                MessageBox.Show("No new bookmarks since the last time!", SJM_BOOKMARK_PLUGIN, MessageBoxButtons.OK, MessageBoxIcon.Information);
            }

            return (String.Format("{0}<p>Auto parsed by <a href = \"http://www.sjmdev.com/\"><strong>delicious bookmark plug-in</strong></a></p>", sb));
        }

        /// <summary>
        /// Saves the users options.
        /// </summary>
        private void SaveOptions()
        {
            Utility.WriteOptionsToStorage(this.Options, _options);
        }

        /// <summary>
        /// Helper method to set Options so they can be updated and the new values used rather than stale
        /// </summary>
        /// <param name="pluginOptions">The plugin options.</param>
        private void SetOptions(IProperties pluginOptions)
        {
            _pluginOptions = pluginOptions;
            _options = Utility.ReadOptionsFromStorage(pluginOptions);

            _username = _options.Username;
            _parameters = _options.Parameters;
            _amountResults = (int)_options.AmountOfResults;
            _lastUsedDate = _options.LastUsedOn;
        }

        /// <summary>
        /// Shows the edit option form and pre-loads it with saved values.
        /// </summary>
        private void ShowEditOptions()
        {
            using (OptionsForm optionForm = new OptionsForm(_options, SJM_BOOKMARK_PLUGIN))
            {
                DialogResult results = optionForm.ShowDialog();
                if (results == DialogResult.OK)
                {
                    _options = optionForm.Options;
                    SaveOptions();
                }
            }
        }

        #endregion

    }
}

